from typing import List, Dict
import os
import json
import sys
from jinja2 import Template
import subprocess
import shutil

from .Util import Util
from .AtomServiceUtil import AtomServiceUtil
from ..entity import AtomService, Message


class DDSUtil:

    @staticmethod
    def generateIdl(atomService: AtomService):
        messages: List[Message] = atomService._messages
        atomic_service_name: str = atomService._base_info.get_name()
        DDSUtil.generateIdlByMessages(messages, atomic_service_name)

    @staticmethod
    def generateIdlByMessages(messages: List[Message], module_name: str):
        """
        通过messages生成idl文件
        :param messages: 消息列表
        :param module_name: 模块名称
        :return:
        """
        # 生成CMakeLists.txt
        idl_template = Template(
            open(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/dds_process_template/idl_template.j2"
            ).read()
        )
        res_str = idl_template.render(
            module_name=module_name,
            messages=messages,
        )
        # 确保目录存在
        os.makedirs(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/atomic_service/{module_name}/idl/",
            exist_ok=True,
        )

        # 将res_str写入框架内的cpp文件中，同名不同路径
        with open(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/atomic_service/{module_name}/idl/{module_name}.idl",
            "w",
        ) as file:
            file.write(res_str)
        print(
            f"SUCCESS: generated {os.path.dirname(os.path.abspath(__file__))}/../../../db/atomic_service/{module_name}/idl/{module_name}.idl"
        )

    @staticmethod
    def generateIdlCMakeLists(atom_service: AtomService):
        atom_service_name = atom_service._base_info.get_name()

        # 生成CMakeLists.txt
        idl_template = Template(
            open(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/dds_process_template/idl_compile_cmake_template.j2"
            ).read()
        )
        res_str = idl_template.render(
            project_name=atom_service_name,
        )

        # 确保目录存在
        os.makedirs(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/atomic_service/{atom_service_name}/idl/",
            exist_ok=True,
        )

        # 将res_str写入框架内的cpp文件中，同名不同路径
        with open(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/atomic_service/{atom_service_name}/idl/CMakeLists.txt",
            "w",
        ) as file:
            file.write(res_str)
        print(
            f"SUCCESS: generated {os.path.dirname(os.path.abspath(__file__))}/../../../db/atomic_service/{atom_service_name}/idl/CMakeLists.txt"
        )

    @staticmethod
    def compileIdl(atom_service: AtomService):
        atom_service_name = atom_service._base_info.get_name()
        file_dir = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/atomic_service/{atom_service_name}/idl/"
        Util.compileCmakeProject(
            file_dir,
            atom_service_name,
            file_type="idl",
        )

    @staticmethod
    def generateClientIdl():
        pass

    @staticmethod
    def compileStructs(orchestrattion_info: dict, client_type: str = "client") -> str:
        """
        将起始节点或末尾节点中需要的结构体编译成idl
        一个节点为一个module，也是一个文件。

        :param orchestrattion_info: orchestrattion_info
        :param client_type: 客户端类型，有client和dds_client
        :return: 生成的idl文件路径
        """
        space_name: str = ""
        if client_type == "client":
            space_name = "client"
        elif client_type == "dds_client":
            space_name = "dds_client"
        else:
            raise Exception("client_type must be client or dds_client")

        # 确保idl目录存在
        idl_path: str = (
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/{space_name}/{orchestrattion_info.get('name', '')}/idl"
        )
        os.makedirs(
            idl_path,
            exist_ok=True,
        )

        # 找到开始或者结束节点
        node_list: list[dict] = list()
        for node in orchestrattion_info.get("nodes", []):
            if node.get("type", "") == "START" or node.get("type", "") == "END":
                node_list.append(node)
        """
        解析输入和输出代码块
        
        导出json
        node_messages_list：所有node的结构体解析出的messages列表
        """
        # 解析节点的结构体为一个message
        node_messages_list: list[List[dict]] = list()
        for node in node_list:
            # 输入字符串
            input_str: str = node.get("struct", "")
            # 通过解析结构体代码块的函数将input_str解析为一个由存储message的dict组成的list
            messages: List[dict] = Util.parseStructCodeBlock(input_str)

            # 保存到json到idl中，命名为start节点id
            # 保存到idl文件夹
            os.makedirs(
                f"{idl_path}/{node.get('module', '')}",
                exist_ok=True,
            )


            node_messages_list.append(messages)

        """
        根据node_messages_list生成idl文件
        """
        # 遍历node_messages_list，生成idl文件
        for i in range(len(node_messages_list)):
            module_name = node_list[i].get("module", "")
            # 遍历message，生成idl文件
            idl_template = Template(
                open(
                    f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/dds_process_template/idl_template.j2"
                ).read()
            )
            # 构建message列表
            messages: List[Message] = list()
            for message in node_messages_list[i]:
                temp = Message()
                temp.set_info(message)
                messages.append(temp)
            AtomServiceUtil._type_convert(messages=messages)
            # 生成idl信息对应的json
            with open(
                f"{idl_path}/{module_name}/{module_name}.json",
                "w",
                encoding="utf-8",
            ) as f:
                f.write(
                    json.dumps(
                        {"module": module_name, "messages": list(msg.to_dict() for msg in messages)}, indent=4, ensure_ascii=False
                    )
                )
            # 生成idl文件
            res_str = idl_template.render(
                module_name=node_list[i].get("module", ""),
                messages=messages,
            )

            with open(
                f"{idl_path}/{module_name}/{module_name}.idl",
                "w",
                encoding="utf-8",
            ) as f:
                f.write(res_str)

            # 生成CMakeLists.txt
            cmake_template = Template(
                open(
                    f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/dds_process_template/idl_compile_cmake_template.j2"
                ).read()
            )
            res_str = cmake_template.render(
                project_name=module_name,
            )
            # 保存到idl文件夹
            with open(
                f"{idl_path}/{module_name}/CMakeLists.txt",
                "w",
                encoding="utf-8",
            ) as f:
                f.write(res_str)

            # 编译idl文件
            Util.compileCmakeProject(
                f"{idl_path}/{module_name}/",
                module_name,
                file_type="idl",
            )

    @staticmethod
    def loadDDS(filePath: str):
        filename_with_ext = os.path.basename(filePath)
        filename_without_ext = os.path.splitext(filename_with_ext)[0]

        # 判断是否存在该文件
        if not os.path.exists(filePath):
            print(filePath + "文件不存在")
            return

        # 将idl文件加载到idl目录下
        # 确保目录存在
        os.makedirs(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/dds/{filename_without_ext}/",
            exist_ok=True,
        )

        # 目标路径
        target_path = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/dds/{filename_without_ext}/{filename_with_ext}"

        # 将文件复制到db中
        try:
            shutil.copy2(filePath, target_path)
            print(f"文件 {filePath} 已成功复制到 {target_path}")
        except FileNotFoundError:
            print(f"源文件 {filePath} 未找到。")
        except PermissionError:
            print("没有足够的权限进行复制操作。")
        except Exception as e:
            print(f"复制文件时出现错误: {e}")

    @staticmethod
    def generateDDSCMakeLists(filename: str, idl_list: List[str]):
        filename = filename.split(".")[0]
        dds_src_path = f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/dds/"
        # 判断是否存在文件和目录
        if not os.path.exists(dds_src_path + filename):
            print(f"目录 {dds_src_path +filename} 不存在")
            return
        if not os.path.exists(os.path.join(dds_src_path, filename, filename + ".cpp")):
            print(
                f"文件 {os.path.join(dds_src_path, filename, filename + '.cpp')} 不存在"
            )
            return
        for idl in idl_list:
            if not os.path.exists(
                os.path.join(dds_src_path, "../idl/", idl, idl + ".idl")
            ):
                print(
                    f"idl {os.path.join(dds_src_path, '../idl/', idl + '.idl')} 不存在"
                )
                return

        # 生成CMakeLists.txt
        idl_template = Template(
            open(
                f"{os.path.dirname(os.path.abspath(__file__))}/../../templates/dds_process_template/dds_src_compile_cmake_template.j2"
            ).read()
        )
        res_str = idl_template.render(project_name=filename, idl_list=idl_list)

        # 确保目录存在
        os.makedirs(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/dds/{filename}/",
            exist_ok=True,
        )

        # 将res_str写入框架内的cpp文件中，同名不同路径
        with open(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/dds/{filename}/CMakeLists.txt",
            "w",
        ) as file:
            file.write(res_str)
        print(
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/dds/{filename}/CMakeLists.txt generated successfully!"
        )

    @staticmethod
    def compileDDS(filename: str):
        filename = filename.split(".")[0]
        file_dir = (
            f"{os.path.dirname(os.path.abspath(__file__))}/../../../db/dds/{filename}/"
        )
        Util.compileCmakeProject(
            file_dir,
            filename,
            file_type="cpp",
        )
